#include "Game.h"
#include "SDL_image.h"
#include "SceneTitle.h"
#include <fstream>

Game::Game()
{
}

void Game::saveData()
{
    // 保存得分榜的数据
    std::ofstream file("assets/save.dat");
    if (!file.is_open()) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open save file");
        return;
    }
    for (const auto &entry : leaderBoard) {
        file << entry.first << " " << entry.second << std::endl;
    }
}

void Game::loadData()
{
    // 加载得分榜的数据
    std::ifstream file("assets/save.dat");
    if (!file.is_open()) {
        SDL_Log("Failed to open save file");
        return;
    }
    leaderBoard.clear();
    int score;
    std::string name;
    while (file >> score >> name) {
        leaderBoard.insert({score, name});
    }
}

Game::~Game()
{
    saveData();
    clean();
}

void Game::run()
{
    while (isRuning) {
        auto frameStart = SDL_GetTicks();
        SDL_Event event;
        handleEvent(&event);

        update(deltaTime);

        render();

        auto frameEnd = SDL_GetTicks();
        auto diff = frameEnd - frameStart;
        if (diff < frameTime) {
            SDL_Delay(frameTime - diff);
            deltaTime = frameTime / 1000.0f;
        } else {
            deltaTime = diff / 1000.0f;
        }
    }
}

void Game::init()
{
    frameTime = 1000 / FPS;

    // SDL Initialize
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR,"SDL_Init error! %s\n", SDL_GetError());
        isRuning = false;
    }
    // Create Window
    window = SDL_CreateWindow("SDL Tutorial", 
                                SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
                                windowWidth, windowHeight, SDL_WINDOW_SHOWN);
    if (window == nullptr) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR,"Create window error! %s\n", SDL_GetError());
        isRuning = false;
    }
    // Create renderer
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (renderer == nullptr) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR,"Create renderer error! %s\n", SDL_GetError());
        isRuning = false;
    }
    // 设置逻辑分辨率
    SDL_RenderSetLogicalSize(renderer, windowWidth, windowHeight);
    
    // SDL_Image Initialize
    if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR,"SDL IMG_Init error! %s\n", SDL_GetError());
        isRuning = false;
    }

    // Initialize SDL_Mixer
    if (Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG) != (MIX_INIT_MP3 | MIX_INIT_OGG)) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL Mix_Init error! %s\n", SDL_GetError());
        isRuning = false;
    }

    // open audio device
    if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL Mix_OpenAudie error! %s\n", SDL_GetError());
        isRuning = false;
    }

    // set sound channel quantity
    Mix_AllocateChannels(32);

    // set music volume
    Mix_VolumeMusic(MIX_MAX_VOLUME/4);
    Mix_Volume(-1, MIX_MAX_VOLUME/8);

    // Initialize SDL_TTF
    if (TTF_Init() == -1) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL TTF_Init error! %s\n", SDL_GetError());
        isRuning = false;
    }

    // 初始化背景卷轴
    nearStars.texture = IMG_LoadTexture(renderer, "assets/image/Stars-A.png");
    SDL_QueryTexture(nearStars.texture, NULL, NULL, &nearStars.width, &nearStars.height);
    nearStars.height /= 2;
    nearStars.width /= 2;

    farStars.texture = IMG_LoadTexture(renderer, "assets/image/Stars-B.png");
    SDL_QueryTexture(farStars.texture, NULL, NULL, &farStars.width, &farStars.height);
    farStars.height /= 2;
    farStars.width /= 2;
    farStars.speed = 20;    // 远处地星星移动速度较慢

    // 载入字体
    titleFont = TTF_OpenFont("assets/font/VonwaonBitmap-16px.ttf", 64);
    textFont = TTF_OpenFont("assets/font/VonwaonBitmap-16px.ttf", 32);
    if (titleFont == nullptr || textFont == nullptr) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont Error: %s\n", TTF_GetError());
        isRuning = false;
    }

    // 载入得分
    loadData();
    
    currentScene = new SceneTitle();
    currentScene->init();
}

void Game::clean()
{
    if (currentScene != nullptr) {
        currentScene->clean();
        delete currentScene;
    }

    if (nearStars.texture != nullptr) {
        SDL_DestroyTexture(nearStars.texture);
    }
    if (farStars.texture != nullptr) {
        SDL_DestroyTexture(farStars.texture);
    }

    // clean SDL_mixer
    Mix_CloseAudio();
    Mix_Quit();

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

void Game::changeScene(Scene *scene)
{
    if (currentScene != nullptr) {
        currentScene->clean();
        delete currentScene;
    }
    currentScene = scene;
    currentScene->init();
}

void Game::handleEvent(SDL_Event* event)
{
    while (SDL_PollEvent(event))
    {
        if (event->type == SDL_QUIT) {
            isRuning = false;
        }
        if (event->type == SDL_KEYDOWN) {
            if (event->key.keysym.scancode == SDL_SCANCODE_F11) {
                isFullscreen = !isFullscreen;
                if (isFullscreen) {
                    SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
                } else {
                    SDL_SetWindowFullscreen(window, 0); 
                }
            }
        }
        currentScene->handleEvent(event);
    }
}

void Game::update(float deltaTime)
{
    backgroundUpdate(deltaTime);
    currentScene->update(deltaTime);
}

void Game::render()
{
    // clear render
    SDL_RenderClear(renderer);
    renderBackground();
    currentScene->render();
    // display update
    SDL_RenderPresent(renderer);
}

void Game::backgroundUpdate(float deltaTime)
{
    nearStars.offset += nearStars.speed * deltaTime;
    if (nearStars.offset >= 0) {
        nearStars.offset -= nearStars.height;
    }
    farStars.offset += farStars.speed * deltaTime;
    if (farStars.offset >= 0) {
        farStars.offset -= farStars.height;
    }
}

void Game::renderBackground()
{
    // 渲染远处地星星
    for (int posY = static_cast<int>(farStars.offset); posY < getWindowHeight(); posY += farStars.height) {
        for (int posX = 0; posX < getWindowWidth(); posX += farStars.width) {
            SDL_Rect ds = {posX, posY, farStars.width, farStars.height};
            SDL_RenderCopy(renderer, farStars.texture, NULL, &ds);
        }
    }

    // 渲染近处地星星
    for (int posY = static_cast<int>(nearStars.offset); posY < getWindowHeight(); posY += nearStars.height) {
        for (int posX = 0; posX < getWindowWidth(); posX += nearStars.width) {
            SDL_Rect ds = {posX, posY, nearStars.width, nearStars.height};
            SDL_RenderCopy(renderer, nearStars.texture, NULL, &ds);
        }
    }
}

SDL_Point Game::renderTextCentered(std::string text, float posY, bool isTitle)
{
    SDL_Color color = {255, 255, 255, 255};
    SDL_Surface *surface;
    if (isTitle) {
        surface = TTF_RenderUTF8_Solid(titleFont, text.c_str(), color);
    } else {
        surface = TTF_RenderUTF8_Solid(textFont, text.c_str(), color);
    }
    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
    int y = static_cast<int>((getWindowHeight()-surface->h) * posY);
    SDL_Rect rect = {getWindowWidth()/2 - surface->w/2,
                    y,
                    surface->w,
                    surface->h};
    SDL_RenderCopy(renderer, texture, NULL, &rect);
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surface);

    return {rect.x + rect.w, y};
}

void Game::renderTextPos(std::string text, int posX, int posY, bool isLeft)
{
    SDL_Color color = {255, 255, 255, 255};
    SDL_Surface *surface = TTF_RenderUTF8_Solid(textFont, text.c_str(), color);
    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_Rect rect;
    if (isLeft) {
        rect = {posX, posY, surface->w, surface->h};
    } else {
        rect = {getWindowWidth() - posX - surface->w, posY, surface->w, surface->h};
    }
    SDL_RenderCopy(renderer, texture, NULL, &rect);
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(surface);
}

void Game::insertLeadBoard(int score, std::string name)
{
    leaderBoard.insert({score, name});
    if (leaderBoard.size() > 8) {
        leaderBoard.erase(--leaderBoard.end());
    }
}
